﻿using System.Collections.Generic;
using System.Linq;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Finder;
using JetBrains.ReSharper.Psi.Search;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Xaml;

namespace GammaJul.ReSharper.GammaXaml.Services.Xaml.Navigation {

	/// <summary>
	/// A searcher that will find referenced XAML fields having a guard method/property.
	/// Without this searcher, nothing will be returned by default since the guard property has a different name than the XAML field (a "Can" prefix).
	/// Eg, searching usages of a CanSave C# property will return the matching XAML Save field bound by Caliburn to a method.
	/// </summary>
	internal sealed class CaliburnGuardReferenceSearcher : IDomainSpecificSearcher {
		private readonly JetHashSet<IDeclaredElement> _elements = new JetHashSet<IDeclaredElement>();
		private readonly HashSet<string> _wordsInFile = new HashSet<string>();
		private readonly HashSet<string> _referenceNames = new HashSet<string>();
		private readonly bool _findCandidates;
		private readonly bool _findLateBound;

		public bool ProcessProjectItem<TResult>(IPsiSourceFile sourceFile, IFindResultConsumer<TResult> consumer) {
			return sourceFile.GetPsiFiles<XamlLanguage>().Any((psiFile => ProcessElement(psiFile, consumer)));
		}

		public bool ProcessElement<TResult>(ITreeNode element, IFindResultConsumer<TResult> consumer) {
			FindExecution findExecution = _findLateBound ?
				new LateBoundReferenceSourceFileProcessor<TResult>(element, consumer, _elements, _wordsInFile, _referenceNames).Run() :
				new ReferenceSearchSourceFileProcessor<TResult>(element, _findCandidates, consumer, _elements, _wordsInFile, _referenceNames).Run();
			return findExecution == FindExecution.Stop;
		}

		public CaliburnGuardReferenceSearcher(IEnumerable<IDeclaredElement> elements, bool findCandidates, bool findLateBound) {
			_findCandidates = findCandidates;
			_findLateBound = findLateBound;
			foreach (IDeclaredElement element in elements) {
				_elements.Add(element);
				string nonGuardSearchName = CaliburnGuardSearcherFactory.GetNonGuardSearchName(element);
				if (nonGuardSearchName != null) {
					_wordsInFile.Add(nonGuardSearchName);
					_referenceNames.Add(element.ShortName);
					_referenceNames.Add(nonGuardSearchName);
				}
			}
		}

	}

}